# -*- coding: utf-8 -*-

"""
@author: Laowang
@contact: QQ:1125564921
@Created on: 2022/6/21
@Remark: 
"""
from abc import abstractmethod
from typing import Callable, Any, Iterable, Union
from concurrent.futures import ThreadPoolExecutor
from dataclasses import Field

from framework_strategy.gopy.dao.orderline import StrategyOrderLine
from framework_strategy.gopy.driver.core import create_engine
from framework_strategy.gopy.gateways.utils import ResponseParamterType
from framework_strategy.gopy.base.constant import EVENT_SENDORDER, EVENT_SENDORDERS, EVENT_CANCELORDERS, EVENT_CANCELORDER

# Middleware template,
# if the implementation of the middle please inherit the middleware template to achieve the method
# 是否考虑单例的模式


class TemplateMiddlewareMeta(type):
    """  """
    @abstractmethod
    def request_before(self, request, *args, **kwargs): ...
    @abstractmethod
    def response_after(self, response, *args, **kwargs): ...


# The structural superclass of middleware
class MiddlewareStructure(metaclass=TemplateMiddlewareMeta):
    """ """
    ...


class TemplateMiddleware(MiddlewareStructure):
    """ """

    def request_before(self, request, *args, **kwargs):
        # print("---request_before---", request, args, kwargs)
        pass

    def response_after(self, response, *args, **kwargs):
        """ """
        # print("---response-after ----", args, kwargs)


class RecordsMiddleware(MiddlewareStructure):
    """
    Intercept the corresponding request events and record the intercepted request events

    TODO
    Need to optimize:  
        *1. Persistence of the model  
        *2. Redundant code  
        *3. Performance optimization  
        *4. Redundant control of connection number 
    """

    def __init__(self) -> None:
        """ """
        super().__init__()
        self.__thread = ThreadPoolExecutor(max_workers=10)
        _, self.__db_session = create_engine()

    def request_before(self, request, *args, **kwargs):
        """ """
        # print("[RecordsMiddleware] ---request_before---", request, args, kwargs)

    def response_after(self, response, *args, **kwargs):
        """ 
        ::param response is tuple len is 2
            * 0 is response type is ResponseObj
            * 1 is Parameter type is BaseData
        """
        event_type = args[1].event_type
        handle = self.match(event_type)

        res = response[0]
        if not handle or not res.status:
            return
        self._RecordsMiddleware__thread.submit(handle, self, response)  # 并发执行
        # handle(self, response)
        # print("[RecordsMiddleware] ---response-after ----", args, kwargs)

    def _save_order(self, response: ResponseParamterType):
        """ """
        res, parameter = response
        event_data = parameter.data.event_data
        param = event_data.extra
        kw = dict()

        # exchange | parameter.data.event_data.exchange
        # market   | parameter.data.event_data.market       | res res.data["data"].market
        # orderid  | ---------------------------------      | res.data["data"].orderId
        # side     | parameter.data.event_data.type         | res res.data["data"].side
        # price    | parameter.data.event_data.price        | res res.data["data"].price
        # number   | parameter.data.event_data.number       | res res.data["data"].number
        # extra    | parameter.data.event_data.extra
        # ----------------------
        # extra = {'app_name': 'test', 'user_name': 'test'}

        if res.status:
            order_response = res.data["data"]
            # TODO 未知其他的订单ID类型
            kw["orderId"] = order_response.orderId

        kw["exchange"] = parameter.data.event_data.exchange
        kw["market"] = param["market"]
        kw["price"] = param["price"]
        kw["number"] = param["number"]
        kw["side"] = "sell" if int(param["type"]) == 0 else "buy"
        if not isinstance(event_data.extra, Field):
            for k in set(dir(StrategyOrderLine)) & event_data.extra.keys():
                kw[k] = event_data.extra[k]

        orderline = self.obj_modle(kw)

        if res.status:
            orderline.detail = format(orderline)
        else:
            orderline.detail = res.response.text
        orderline.status = True

        orderline.response_content = res.response.text
        self._RecordsMiddleware__db_session.add(orderline)
        self._RecordsMiddleware__db_session.commit()

    def _save_orders(self, response: ResponseParamterType):
        """ """

        # exchange | parameter.data.event_data.exchange
        # market   | parameter.data.event_data.market       | res.event.event_data.market
        # orderid  | ---------------------------------      | res res.data["data"][0].orderId
        # side     | parameter.data.event_data.type         | res res.data["data"][slice].side
        # price    | parameter.data.event_data.price        | res res.data["data"][0].price
        # number   | parameter.data.event_data.number       | res res.data["data"][0].quantity
        # extra    | parameter.data.event_data.extra
        # ----------------------
        # extra = {'app_name': 'test', 'user_name': 'test'}

        res, parameter = response
        event_data = parameter.data.event_data

        if not res.status:
            kw = dict()
            kw["exchange"] = event_data.exchange
            kw["market"] = event_data.market

            if not isinstance(event_data.extra, Field):
                for k in set(dir(StrategyOrderLine)) & event_data.extra.keys():
                    kw[k] = event_data.extra[k]

            orderline = self.obj_modle(kw)
            orderline.response_content = res.response.text
            self._RecordsMiddleware__db_session.add(orderline)
            self._RecordsMiddleware__db_session.commit()
            return

        data = res.data["data"]
        objs = []

        for order_response in data:
            kw = dict()

            kw["orderId"] = order_response.orderId
            kw["exchange"] = event_data.exchange
            kw["market"] = res.event.event_data.market
            kw["price"] = order_response.price
            kw["number"] = order_response.quantity
            kw["side"] = "sell" if order_response.side == 0 else "buy"

            if not isinstance(event_data.extra, Field):
                for k in set(dir(StrategyOrderLine)) & event_data.extra.keys():
                    kw[k] = event_data.extra[k]

            orderline = self.obj_modle(kw)
            objs.append(orderline)
            if res.status:
                orderline.detail = format(orderline)
            else:
                orderline.detail = res.response.text
            orderline.response_content = res.response.text
            orderline.status = res.status
        self._RecordsMiddleware__db_session.add_all(objs)
        self._RecordsMiddleware__db_session.commit()

    def obj_modle(self, kw):

        orderline = StrategyOrderLine(**kw)
        orderline.detail = format(orderline)
        return orderline

    def query_obj(self, order_id, content, uuid, detail):
        # 查obj
        queryset = self._RecordsMiddleware__db_session.query(
            StrategyOrderLine).filter(StrategyOrderLine.orderId == order_id)
        if queryset.count() <= 0:
            return
        obj = queryset.first()

        # 更新
        orderline = StrategyOrderLine(
            uuid=uuid,
            orderId=obj.orderId,
            exchange=obj.exchange,
            market=obj.market,
            side=obj.side,
            price=obj.price,
            number=obj.number,
            app_name=obj.app_name,
            user_name=obj.user_name,
            is_contract=obj.is_contract,
            note=obj.note,
            detail=detail.format(obj),
            response_content=content,
            status=True,
        )
        return orderline

    def _save_cancel_order(self, response: ResponseParamterType):
        """ """

        # exchange | parameter.data.event_data.exchange
        # market   | parameter.data.event_data.market       | res.event.event_data.market
        # orderid  | parameter.data.event_data.id           | res res.data["data"].orderId
        # side     | parameter.data.event_data.type         | # obj.
        # price    | parameter.data.event_data.price        | # obj.
        # number   | parameter.data.event_data.number       | # obj.
        # extra    | parameter.data.event_data.extra
        # ----------------------
        # extra = {'app_name': 'test', 'user_name': 'test'}

        res, parameter = response
        event_data = parameter.data.event_data

        detail = "平台 {0.exchange!r} 订单: {0.orderId!r} 取消"
        if not res.status:
            detail = "平台 {0.exchange!r} 订单: {0.orderId!r} 取消失败"

        id = parameter.data.event_data.id
        content = res.response.text

        if isinstance(event_data.extra, Field):
            uuid = None
        else:
            uuid = event_data.extra.get("uuid")

        orderline = self.query_obj(id, content, uuid, detail)
        if not orderline:
            return
        orderline.status = res.status
        self._RecordsMiddleware__db_session.add(orderline)
        self._RecordsMiddleware__db_session.commit()

    def _save_cancel_orders(self, response: ResponseParamterType):
        """ """

        # exchange | parameter.data.event_data.exchange
        # market   | parameter.data.event_data.market       | res.event.event_data.market
        # orderid  | parameter.data.event_data.id           | res res.data["data"].orderId
        # side     | parameter.data.event_data.type         | # obj.
        # price    | parameter.data.event_data.price        | # obj.
        # number   | parameter.data.event_data.number       | # obj.
        # extra    | parameter.data.event_data.extra
        # ----------------------
        # extra = {'app_name': 'test', 'user_name': 'test'}

        res, parameter = response
        event_data = parameter.data.event_data

        if not res.status:
            kw = dict()
            kw["exchange"] = event_data.exchange
            kw["market"] = event_data.market

            if not isinstance(event_data.extra, Field):
                for k in set(dir(StrategyOrderLine)) & event_data.extra.keys():
                    kw[k] = event_data.extra[k]
            orderline = self.obj_modle(kw)
            orderline.response_content = res.response.text
            self._RecordsMiddleware__db_session.add(orderline)
            self._RecordsMiddleware__db_session.commit()
            return

        objs = []
        content = res.response.text
        if isinstance(event_data.extra, Field):
            uuid = None
        else:
            uuid = event_data.extra.get("uuid")
        for order in res.data["data"]:

            detail = "平台 {0.exchange!r} 订单: {0.orderId!r} 取消"
            objs.append(self.query_obj(order.orderId, content, uuid, detail))

        self._RecordsMiddleware__db_session.add_all(objs)
        self._RecordsMiddleware__db_session.commit()

    def match(self, event_type: str) -> Union[Callable[[Any], None], None]:
        """ """
        for __tuple in self.record_events():
            # TODO To optimize
            if event_type.endswith(__tuple[0]):
                return __tuple[1]

    def record_events(self) -> Iterable:
        """ """
        yield from self.EVENT_RECORDS

    EVENT_RECORDS = [
        (EVENT_SENDORDER, _save_order),
        (EVENT_SENDORDERS, _save_orders),
        (EVENT_CANCELORDERS, _save_cancel_orders),
        (EVENT_CANCELORDER, _save_cancel_order),
    ]
